package logic

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/mqueuex"
	"mall/service/attachment/model"
	"mall/service/attachment/rpc/internal/svc"
	"mall/service/attachment/rpc/internal/utils/errorhandler"
	"mall/service/attachment/rpc/types/attachment"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type ScanFileLogLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewScanFileLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ScanFileLogLogic {
	return &ScanFileLogLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *ScanFileLogLogic) ScanFileLog(in *attachment.ScanFileLogReq) (*attachment.BaseResp, error) {
	var orderBys []string
	if len(in.OrderBy) > 0 {
		for _, orderBy := range in.OrderBy {
			order := " ASC"
			if orderBy.Order == "1" {
				order = " DESC"
			}

			switch orderBy.Field {
			case "id":
				orderBys = append(orderBys, model.AttachmentLogColumns.LogID+order)
			case "fid":
				orderBys = append(orderBys, model.AttachmentLogColumns.AttachmentID+order)
			case "pid":
				orderBys = append(orderBys, model.AttachmentLogColumns.ParentID+order)
			case "uid":
				orderBys = append(orderBys, model.AttachmentLogColumns.UserID+order)
			}
		}
	}
	if len(orderBys) == 0 {
		orderBys = append(orderBys, model.AttachmentLogColumns.LogID)
	}

	var stati string
	if len(in.FilterBy) > 0 {
		for _, filterBy := range in.FilterBy {
			if filterBy.Field == "status" {
				stati = strings.TrimSpace(filterBy.In)
			}
		}
	}

	var page uint64
	var pageSize uint64
	var restricted bool
	filterMap := make(map[string]map[string][]any)
	resp := &attachment.BaseResp{}
	if in.Ids != nil {
		if vals := modelx.UniqueInterfaces(*in.Ids, 64); len(vals) == 0 {
			return resp, nil
		} else {
			filterMap[model.AttachmentLogColumns.LogID] = map[string][]any{
				"in": vals,
			}
			restricted = true
		}
	}

	if !restricted {
		page = in.Page
		pageSize = in.PageSize
	}

	statusIn := modelx.UniqueInterfaces(stati, 32)
	statusMap := make(map[string][]any)
	if len(statusIn) > 0 {
		statusMap["in"] = statusIn
	}
	if in.Status != nil {
		statusMap["equalTo"] = []any{*in.Status}
	} else {
		statusMap["equalTo"] = []any{globalkey.StatusEnabled}
	}
	filterMap[model.AttachmentLogColumns.Status] = statusMap

	if in.ParentId != nil && len(*in.ParentId) > 0 {
		if i, err := strconv.ParseInt(*in.ParentId, 10, 64); err == nil {
			filterMap[model.AttachmentLogColumns.ParentID] = map[string][]any{
				"equalTo": {i},
			}
		}
	}
	if in.UserId != nil && len(*in.UserId) > 0 {
		if i, err := strconv.ParseInt(*in.UserId, 10, 64); err == nil {
			filterMap[model.AttachmentLogColumns.UserID] = map[string][]any{
				"equalTo": {i},
			}
		}
	}
	if in.Type != nil {
		filterMap[model.AttachmentLogColumns.Type] = map[string][]any{
			"equalTo": {*in.Type},
		}
	}
	if in.Step != nil {
		filterMap[model.AttachmentLogColumns.RMQStep] = map[string][]any{
			"equalTo": {*in.Step},
		}
	} else {
		filterMap[model.AttachmentLogColumns.RMQStep] = map[string][]any{
			"lessThan": {globalkey.RmqFinalStep},
		}
	}
	filterMap[model.AttachmentLogColumns.FailedCount] = map[string][]any{
		"lessThan": {globalkey.RmqMaxFailedCount},
	}

	var orderBy string
	if len(orderBys) > 0 {
		orderBy = strings.Join(orderBys, ",")
	}
	logs, _, err := l.svcCtx.FileLog.GetList(l.ctx, model.M{
		"page":     page,
		"pageSize": pageSize,
		"orderBy":  orderBy,
	}, -1, filterMap)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	var fids []any
	for _, log := range logs {
		fids = append(fids, log.AttachmentID)
	}

	if len(fids) == 0 {
		return resp, nil
	}

	fileFilterMap := make(map[string]map[string][]any)
	fileFilterMap[model.AttachmentColumns.ID] = map[string][]any{
		"in": fids,
	}
	list, _, err := l.svcCtx.MediaFile.GetList(l.ctx, model.M{}, -1, fileFilterMap)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	typeMap := make(map[uint64]int32)
	urlMap := make(map[uint64]string)
	for _, v := range list {
		typeMap[v.ID] = int32(v.PostType)
		if len(v.GUID) > 0 {
			urlMap[v.ID] = l.svcCtx.Config.QiniuConf.Domain + v.GUID
		}
	}

	// tx, err := l.svcCtx.DB.BeginTx(l.ctx, nil)
	// if err != nil {
	// 	return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	// }

	for _, log := range logs {
		if fileType, ok := typeMap[log.AttachmentID]; ok {
			finalType, put, thumbnail, watermark, step := l.parseQiniuFileType(int32(log.Type), fileType)
			// logrus.Info(fmt.Sprintf("ScanFileLog parseQiniuFileType log.Type: %+v", log.Type))
			// logrus.Info(fmt.Sprintf("ScanFileLog parseQiniuFileType fileType: %+v", fileType))
			// logrus.Info(fmt.Sprintf("ScanFileLog parseQiniuFileType finalType: %+v", finalType))
			// logrus.Info(fmt.Sprintf("ScanFileLog parseQiniuFileType put: %+v", put))
			// logrus.Info(fmt.Sprintf("ScanFileLog parseQiniuFileType thumbnail: %+v", thumbnail))
			// logrus.Info(fmt.Sprintf("ScanFileLog parseQiniuFileType watermark: %+v", watermark))
			// logrus.Info(fmt.Sprintf("ScanFileLog parseQiniuFileType step: %+v", step))
			// logrus.Info(fmt.Sprintf("============================================================\n"))

			if log.RMQStep > 0 && fileType >= finalType { // log.RMQStep == 0 先判断文件类型和当前实际状态
				if len(log.KeyValue) == 0 {
					// blog、product Sync Attachment
					if url, ok := urlMap[log.AttachmentID]; ok {
						// alt := l.parseAlt(log.MetaValue)
						if err = l.pubKqAttachmentSyncQiniuExtra(log.AttachmentID, int32(log.ObjectType), log.ObjectID, url); err != nil {
							logrus.Info(fmt.Sprintf("ScanFileLog pubKqAttachmentSyncQiniuExtra err: %+v", err))
							logx.Errorw("ScanFileLog pubKqAttachmentSyncQiniuExtra err", logx.Field("detail", err.Error()))
							continue
						}

						log.KeyValue = url
					}
				}

				log.RMQStep = uint8(globalkey.RmqFinalStep)
				err = l.svcCtx.FileLog.Update(l.ctx, nil, log)
				if err != nil {
					// tx.Rollback()
					// return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
					logrus.Info(fmt.Sprintf("ScanFileLog FileLog.Update error: %+v", err))
					logx.Errorw("ScanFileLog FileLog.Update error", logx.Field("detail", err.Error()))
				}
			} else { // log.RMQStep == 0 || (log.RMQStep > 0 && fileType < finalType)
				if log.RMQStep > step { // log.RMQStep > step && fileType < finalType
					logrus.Info(fmt.Sprintf("ScanFileLog error log: %+v", log))
					continue
				}

				if (put + thumbnail + watermark) > 0 {
					if err = l.pubKqAttachmentSyncQiniu(log.AttachmentID, put, thumbnail, watermark); err != nil {
						logrus.Info(fmt.Sprintf("ScanFileLog pubKqAttachmentSyncQiniu err: %+v", err))
						logx.Errorw("ScanFileLog pubKqAttachmentSyncQiniu err", logx.Field("detail", err.Error()))
						log.FailedCount += 1
						err = l.svcCtx.FileLog.Update(l.ctx, nil, log)
						if err != nil {
							logrus.Info(fmt.Sprintf("ScanFileLog FileLog.Update err: %+v", err))
							logx.Errorw("ScanFileLog FileLog.Update err", logx.Field("detail", err.Error()))
						}
						continue
					}
				}

				if log.RMQStep < step {
					if log.RMQStep == uint8(0) {
						if url, ok := urlMap[log.AttachmentID]; ok {
							log.KeyValue = url // 如果刚开始就有KeyValue，记录下来，后续不需要通知其他微服务
						}
					}
					log.RMQStep = step // 如果类型错误，会直接设置为 globalkey.RmqFinalStep
					err = l.svcCtx.FileLog.Update(l.ctx, nil, log)
					if err != nil {
						// tx.Rollback()
						// return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
						logrus.Info(fmt.Sprintf("ScanFileLog FileLog.Update err: %+v", err))
						logx.Errorw("ScanFileLog FileLog.Update err", logx.Field("detail", err.Error()))
					}
				}
			}
		}
	}

	// tx.Commit()

	return resp, nil
}

func (l *ScanFileLogLogic) pubKqAttachmentSyncQiniu(id uint64, put uint32, thumbnail uint32, watermark uint32) error {
	m := mqueuex.AttachmentSyncQiniuMessage{
		Id:        id,
		Put:       put,
		Watermark: watermark,
		Thumbnail: thumbnail,
	}
	body, err := json.Marshal(m)
	if err != nil {
		return errors.New("ScanFileLog pubKqAttachmentSyncQiniu json.Marshal error")
		// return errors.Wrapf(errorx.NewDefaultError("kq AttachmentRpc.ScanFileLog pubKqAttachmentSyncQiniu task marshal error"), "kq AttachmentRpc.UpdateLogic pushKqSyncQiniu task marshal error, v: %+v ", m)
	}

	return l.svcCtx.KqAttachmentSyncQiniuClient.Push(string(body))
}

func (l *ScanFileLogLogic) pubKqAttachmentSyncQiniuExtra(id uint64, objType int32, objId uint64, url string) error {
	m := mqueuex.AttachmentSyncQiniuMessage{
		Id:     id,
		Type:   objType,
		TypeId: objId,
		Url:    url,
	}
	body, err := json.Marshal(m)
	if err != nil {
		return errors.New("ScanFileLog pubKqAttachmentSyncQiniuExtra json.Marshal error")
	}

	return l.svcCtx.KqAttachmentSyncQiniuClient.Push(string(body))
}

func (l *ScanFileLogLogic) parseQiniuFileType(
	fileType int32,
	currentFileType int32,
) (finalType int32, put uint32, thumbnail uint32, watermark uint32, step uint8) {
	switch fileType {
	case globalkey.AttachmentTypeLocalTempFile, globalkey.AttachmentTypeLocalFile:
		finalType = globalkey.AttachmentTypeLocalFile
		step = uint8(globalkey.RmqFinalStep)
	case globalkey.AttachmentTypePutPendingFile:
		finalType = globalkey.AttachmentTypePutReadyFile
		if l.putted(currentFileType) {
			step = 1 // 当前执行到第一步
		} else {
			put = 1  // 尚未推送
			step = 1 // 当前执行到第一步
		}
	case globalkey.AttachmentTypePutReadyFile:
		finalType = globalkey.AttachmentTypePutReadyFile
		step = 1
	case globalkey.AttachmentTypeWatermarkPendingFile:
		finalType = globalkey.AttachmentTypeWatermarkReadyFile
		if l.putted(currentFileType) {
			step = 1
			if l.watermarked(currentFileType) {
				step = 2
			} else {
				watermark = 1 // 尚未打水印
				step = 2      // 当前执行到第二步
			}
		} else {
			put = 1
			step = 1
		}
	case globalkey.AttachmentTypeWatermarkPendingPutReadyFile:
		finalType = globalkey.AttachmentTypeWatermarkReadyFile
		if l.watermarked(currentFileType) {
			step = 2
		} else {
			watermark = 1
			step = 2
		}
	case globalkey.AttachmentTypeWatermarkReadyFile:
		finalType = globalkey.AttachmentTypeWatermarkReadyFile
		step = 2
	case globalkey.AttachmentTypeThumbnailPendingFile:
		finalType = globalkey.AttachmentTypeThumbnailReadyFile
		if l.putted(currentFileType) {
			step = 1
			if l.thumbnailed(currentFileType) {
				step = 3
			} else {
				thumbnail = 1 // 尚未生成缩略图
				step = 3      // 当前执行到第三步
			}
		} else {
			put = 1
			step = 1
		}
	case globalkey.AttachmentTypeThumbnailPendingPutReadyFile:
		finalType = globalkey.AttachmentTypeThumbnailReadyFile
		if l.thumbnailed(currentFileType) {
			step = 3
		} else {
			thumbnail = 1
			step = 3
		}
	case globalkey.AttachmentTypeThumbnailReadyFile:
		finalType = globalkey.AttachmentTypeThumbnailReadyFile
		step = 3
	case globalkey.AttachmentTypePendingFile:
		finalType = globalkey.AttachmentTypeReadyFile
		if l.putted(currentFileType) {
			step = 1
			if l.watermarked(currentFileType) {
				step = 4
				if l.thumbnailed(currentFileType) {
					step = 5
				} else {
					thumbnail = 1 // 尚未生成缩略图
					step = 5      // 当前执行到第五步
				}
			} else {
				watermark = 1 // 尚未打水印
				step = 4      // 当前执行到第四步
			}
		} else {
			put = 1
			step = 1
		}
	case globalkey.AttachmentTypePendingPutReadyFile:
		finalType = globalkey.AttachmentTypeReadyFile
		if l.watermarked(currentFileType) {
			step = 4
			if l.thumbnailed(currentFileType) {
				step = 5
			} else {
				thumbnail = 1
				step = 5
			}
		} else {
			watermark = 1
			step = 4
		}
	case globalkey.AttachmentTypePendingWatermarkReadyFile:
		finalType = globalkey.AttachmentTypeReadyFile
		if l.thumbnailed(currentFileType) {
			step = 5
		} else {
			thumbnail = 1
			step = 5
		}
	case globalkey.AttachmentTypeReadyFile:
		finalType = globalkey.AttachmentTypeReadyFile
		step = 5
	default:
		step = uint8(globalkey.RmqFinalStep)
	}

	return
}

func (l *ScanFileLogLogic) putted(
	fileType int32,
) bool { // 已推送到七牛云
	if fileType == globalkey.AttachmentTypePutReadyFile || fileType == globalkey.AttachmentTypeWatermarkPendingPutReadyFile ||
		fileType == globalkey.AttachmentTypeThumbnailPendingPutReadyFile ||
		fileType == globalkey.AttachmentTypePendingPutReadyFile {
		return true
	}

	return false
}

func (l *ScanFileLogLogic) watermarked(
	fileType int32,
) bool { // 已打水印
	if fileType == globalkey.AttachmentTypeWatermarkReadyFile ||
		fileType == globalkey.AttachmentTypePendingWatermarkReadyFile {
		return true
	}

	return false
}

func (l *ScanFileLogLogic) thumbnailed(
	fileType int32,
) bool { // 已生成缩略图
	if fileType == globalkey.AttachmentTypeThumbnailReadyFile ||
		fileType == globalkey.AttachmentTypeReadyFile {
		return true
	}

	return false
}
